package model.dominio.arbolHuffman;

/**
 * Clase que genera el arbol de Huffman
 *
 * @author Gustavo Canul, Noe Cob, Arandi López
 *
 */
public class ArbolBinarioHuffman {

    public NodoBinarioHuffman primerElemento, ultimoElemento;
    public NodoBinarioHuffman raizArbol;
    public byte tablaBytes[][];
    public String listaCodigosBinarios[];
    private int tamanoArbol;
    private int contador;

    /**
     * Constructor que inicializa los valores de los nodos
     *
     * @param primerElemento
     */
    public ArbolBinarioHuffman(NodoBinarioHuffman primerElemento) {
        tamanoArbol = 0;
        contador = 0;
    }

    /**
     * Constructor que añade nodos a la lista
     *
     * @param miNodo
     */
    public void agregarElemento(NodoBinarioHuffman miNodo) {
        if (primerElemento == null) {
            primerElemento = miNodo;
            ultimoElemento = miNodo;
        } else {
            NodoBinarioHuffman aux = primerElemento;
            while (aux.getNodoDerecho() != null) {
                aux = aux.getNodoDerecho();
            }
            aux.setNodoDerecho(miNodo);
            miNodo.setNodoIzquierdo(aux);
            ultimoElemento = miNodo;
        }
        tamanoArbol++;
    }

    /**
     * Metodo que ordena la lista de menor a mayor
     *
     */
    public void ordenarArbol() {

        NodoBinarioHuffman aux1 = primerElemento;
        NodoBinarioHuffman aux2 = aux1;
        int tempFrec;
        char tempLetra;

        while (aux1 != null) {

            while (aux2.getNodoDerecho() != null) {
                if (aux2.getFrecuencia() >= aux2.getNodoDerecho().getFrecuencia()) {
                    tempFrec = aux2.getFrecuencia();
                    tempLetra = aux2.getCaracter();
                    aux2.setFrecuencia(aux2.getNodoDerecho().getFrecuencia());
                    aux2.setCaracter(aux2.getNodoDerecho().getCaracter());
                    aux2.getNodoDerecho().setFrecuencia(tempFrec);
                    aux2.getNodoDerecho().setCaracter(tempLetra);

                    if (aux2.getCaracter() > aux2.getNodoDerecho().getCaracter() && aux2.getFrecuencia() == aux2.getNodoDerecho().getFrecuencia()) {
                        tempFrec = aux2.getFrecuencia();
                        tempLetra = aux2.getCaracter();
                        aux2.setFrecuencia(aux2.getNodoDerecho().getFrecuencia());
                        aux2.setCaracter(aux2.getNodoDerecho().getCaracter());
                        aux2.getNodoDerecho().setFrecuencia(tempFrec);
                        aux2.getNodoDerecho().setCaracter(tempLetra);
                    }
                }
                aux2 = aux2.getNodoDerecho();
            }
            aux2 = primerElemento;
            aux1 = aux1.getNodoDerecho();
        }
    }

    /**
     * Metodo que genera los codigos del arbol de Huffman
     */
    public void generarArbol() {

        NodoBinarioHuffman operador1 = primerElemento, operador2;
        tablaBytes = new byte[tamanoArbol][3];
        listaCodigosBinarios = new String[tamanoArbol];
        int peso = 0;

        while (operador1.getNodoDerecho() != null) {
            if (operador1.getNodoDerecho() != null) {
                peso = operador1.getFrecuencia() + operador1.getNodoDerecho().getFrecuencia();
            } else {
                peso = operador1.getFrecuencia();
            }

            operador2 = new NodoBinarioHuffman((char) 0, peso, false);
            operador2 = ubicarNodo(operador2, operador1);
            operador2.setHijoIzquierdo(operador1);
            operador2.setHijoDerecho(operador1.getNodoDerecho());
            operador1 = operador1.getNodoDerecho().getNodoDerecho();

            if (operador1.getNodoDerecho() == null) {
                raizArbol = operador2;
            }
        }
        generarCodigoBinario(raizArbol, "");
    }

    /**
     * Metodo que ubica el nodo en la lista 
     *
     * @param nodo 
     * @param nodoRegistro 
     * @return  nodo 
     */
    public NodoBinarioHuffman ubicarNodo(NodoBinarioHuffman nodo, NodoBinarioHuffman nodoRegistro) {
        NodoBinarioHuffman nodoOperador = nodoRegistro;

        while (nodoOperador != null) {
            if (nodo.getFrecuencia() <= nodoOperador.getFrecuencia()) {
                nodoOperador.getNodoIzquierdo().setNodoDerecho(nodo);
                nodo.setNodoIzquierdo(nodoOperador.getNodoIzquierdo());
                nodoOperador.setNodoIzquierdo(nodo);
                nodo.setNodoDerecho(nodoOperador);
                return nodo;
            }
            if (nodoOperador.getNodoDerecho() == null) {
                nodoOperador.setNodoDerecho(nodo);
                nodo.setNodoIzquierdo(nodoOperador);
                ultimoElemento = nodo;
                break;
            }
            nodoOperador = nodoOperador.getNodoDerecho();
        }
        return nodo;
    }

    /**
     * Metodo que genera los codigos del arbol de Huffman  
     * 
     * @param nodo 
     * @param cadena 
     */
    public void generarCodigoBinario(NodoBinarioHuffman nodo, String cadena) {

        if (nodo != null) {
            if (nodo.isHoja()) {
                nodo.setCodigo(cadena);
                tablaBytes[contador][0] = (byte) nodo.getCaracter();
                tablaBytes[contador][1] = (byte) nodo.getFrecuencia();
                listaCodigosBinarios[contador] = cadena;
                contador++;
            }
            generarCodigoBinario(nodo.getHijoIzquierdo(), cadena + "0");
            generarCodigoBinario(nodo.getHijoDerecho(), cadena + "1");
        }
    }

    /**
     * Metodo que obtiene el codigo de un byte determinado
     *
     * @param valor
     * @return null
     */
    public String obtenerCodigoBinario(byte valor) {
        for (int i = 0; i < tamanoArbol; i++) {
            if (tablaBytes[i][0] == valor) {
                return listaCodigosBinarios[i];
            }
        }
        return null;
    }

    /**
     * Metodo que devuelve la tabla de frecuencias
     *
     * @return tsblsBytes
     */
    public byte[][] getTablaBytes() {
        return tablaBytes;
    }

    /**
     *Metodo que devuelve el numero de ndodos en el arbol
     *
     * @return tamanoArbol
     */
    public int getTamanoArbol() {
        return tamanoArbol;
    }
}
